/*    

    bobbietalk - a visual programming environment for toy robotics

    Copyright (C) 2013  Roberto Tiella

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

*/
package bobbietalk.gui;

import bobbietalk.Utilities;
import bobbietalk.gui.blocks.ProgramLoader;
import bobbietalk.gui.dialogs.UserMessagesDialog;
import bobbietalk.gui.dialogs.BlockEditingDialog;
import bobbietalk.gui.dialogs.ConfigureRobotDialog;
import bobbietalk.gui.dialogs.InterpreterControlDialog;
import bobbietalk.gui.dialogs.LocalSimulationWindow;
import bobbietalk.gui.dialogs.ConfigureApplicationDialog;
import bobbietalk.gui.dialogs.UserInputDialog;
import bobbietalk.gui.blocks.parametrized.BlockFactory;
import bobbietalk.gui.blocks.parametrized.ParametrizedBlock;
import bobbietalk.semantics.compiler.NxcCompiler;
import bobbietalk.gui.undo.UnduableActionEvent;
import bobbietalk.gui.undo.UnduableActionsListener;
import bobbietalk.semantics.compiler.BshCompiler;
import java.awt.Font;
import java.util.List;
import java.util.Locale;
import java.util.ResourceBundle;
import java.util.logging.Level;
import javax.swing.JFileChooser;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import bobbietalk.semantics.compiler.Compiler;
import bobbietalk.semantics.compiler.PseudoCodeCompiler;
import bobbietalk.semantics.interpreter.robots.remote.InterpreterController;
import bobbietalk.xsd.Palette;
import java.awt.Component;
import java.awt.Container;
import java.awt.Desktop;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.StringReader;
import java.io.StringWriter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.MissingResourceException;
import java.util.Properties;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.xml.bind.JAXBException;

/**
 *
 * @author roberto
 */
@SuppressWarnings("serial")
public class MainForm extends javax.swing.JFrame implements UnduableActionsListener, SelectionChangedListener, ProgramChangedListener {

    private ResourceBundle bundle = java.util.ResourceBundle.getBundle("bobbietalk/Labels");
    private UserMessagesDialog userDialog = new UserMessagesDialog(this, true);
    private UserInputDialog userInputDialog = new UserInputDialog(this, true);
    private ConfigureApplicationDialog configureApplicationDialog = new ConfigureApplicationDialog(this, true);
    private ConfigureRobotDialog configureRobotDialog = new ConfigureRobotDialog(this, true);
    private InterpreterControlDialog interpreterControlDialog = new InterpreterControlDialog(this, true);
    private BlockEditingDialog blockEditingDialog = new BlockEditingDialog(this, true);
    private LocalSimulationWindow localSimulationWindow = new LocalSimulationWindow(this, false);
    // private ProgrammingBlock selected = null;
    private float fontSize;
    public Properties properties;
    private int untitledFileNumber = 1;
    private File programFile;
    private String programName;
    private BlockFactory blockFactory;
    private boolean neverSaved = true;
    private String language;
    private String paletteFilename = "palette.xml";

    /**
     * Creates new form MainForm
     */
    public MainForm() throws Exception {

        properties = new Properties();

        String os = System.getProperty("os.name").toLowerCase().replace("[^A-Za-z0-9]", "");
        String hn = InetAddress.getLocalHost().getHostName();

        String propertiesFileName = String.format("bobbietalk.%s.%s.properties", os, hn);

        final ClassLoader classLoader = this.getClass().getClassLoader();

        InputStream ras = classLoader.getResourceAsStream(propertiesFileName);

        if (ras == null) {

            propertiesFileName = String.format("bobbietalk.%s.properties", os);

            ras = classLoader.getResourceAsStream(propertiesFileName);

            if (ras == null) {

                throw new IllegalStateException("can't find config:" + propertiesFileName + " file, add config directory to the classpath, please.");

            }
        }

        System.out.println("loading properties from file:" + propertiesFileName);
        properties.load(ras);

        fontSize = Float.parseFloat(properties.getProperty("interface.font.size"));
        language = properties.getProperty("interface.language");
        paletteFilename = properties.getProperty("interface.palette.filename");

        updateApplicationConfiguration();

        newProgram();
        updateTitle();
        updateMenuState();

    }

    private void myInitComponents() throws JAXBException, IOException, BobbietalkException {

        disposeDialogs();

        userDialog = new UserMessagesDialog(this, true);
        userInputDialog = new UserInputDialog(this, true);
        configureApplicationDialog = new ConfigureApplicationDialog(this, true);
        configureRobotDialog = new ConfigureRobotDialog(this, true);
        interpreterControlDialog = new InterpreterControlDialog(this, true);
        blockEditingDialog = new BlockEditingDialog(this, true);
        localSimulationWindow = new LocalSimulationWindow(this, false);
        final Container contentPane = this.getContentPane();

//        for (int i = 0; i < this.getContentPane().getComponentCount(); i++) {
//            
//            Component component = this.getContentPane().getComponent(i);
//            // this.getContentPane().getLayout().removeLayoutComponent(component);
//            
//            System.out.println("removing:"+component.toString());
//            
//            this.getContentPane().remove(component);
//            
//
//        }

        if (contentPane.getComponentCount() > 0) {

            contentPane.remove(palettePanel);
            contentPane.remove(programmingBoardScrollPane);

        }

        initComponents();

        addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {

                checkProgramSavedAndAskUserToClose();

            }
        });


        setupPaletteMenu();

        programmingBoard.addUnduableActionsListener(this);
        programmingBoard.addSelectionChangedListener(this);
        programmingBoard.addProgramChangedListener(this);
        programmingBoard.setMainFrame(this);
        programmingBoard.setGridEnabled(true);

        // setFonts(fontSize);

        setFontsInMenu(fontSize);
        setFontsByComponent(this, fontSize);
        setFontsByComponent(userDialog, fontSize);
        userDialog.pack();
        setFontsByComponent(userInputDialog, fontSize);
        userInputDialog.pack();
        setFontsByComponent(configureApplicationDialog, fontSize);
        configureApplicationDialog.pack();

        aboutDialog.pack();

        userDialog.setLocation(userDialog.getParent().getWidth() / 2, userDialog.getParent().getHeight() / 2);
        userInputDialog.setLocation(userInputDialog.getParent().getWidth() / 2, userInputDialog.getParent().getHeight() / 2);
        configureApplicationDialog.setLocation(configureApplicationDialog.getParent().getWidth() / 2, configureApplicationDialog.getParent().getHeight() / 2);
        configureRobotDialog.setLocation(configureRobotDialog.getParent().getWidth() / 2, configureRobotDialog.getParent().getHeight() / 2);


        if (!properties.getProperty("interface.mode").equals("developer")) {

            fileDumpMenuItem.setVisible(false);
            showBshProgramMenuItem.setVisible(false);
            showNxcProgramMenuItem.setVisible(false);
            runInRemoteSimulatorMenuItem.setVisible(false);
            runInLocalSimulatorMenuItem.setVisible(false);

        }

    }

    public void setFontsInMenu(float fontSize) {

        for (int i = 0; i < jMenuBar1.getMenuCount(); i++) {

            final JMenu menu = jMenuBar1.getMenu(i);

            menu.setFont(menu.getFont().deriveFont(fontSize));

            for (int j = 0; j < menu.getItemCount(); j++) {

                JMenuItem item = menu.getItem(j);

                if (item != null) {

                    final Font font = item.getFont();

                    item.setFont(font.deriveFont(fontSize));
                }

            }

        }
    }

    public void setFontsByComponent(Container parent, float fontSize) {


        for (int i = 0; i < parent.getComponentCount(); i++) {

            final Component component = parent.getComponent(i);

            try {

                Method setFontMethod = component.getClass().getMethod("setFont", Font.class);
                Method getFontMethod = parent.getComponent(i).getClass().getMethod("getFont");

                Font f = (Font) getFontMethod.invoke(component);

                setFontMethod.invoke(component, f.deriveFont(fontSize));

//                System.out.println("setting font to: " + component);




            } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
                Utilities.getLogger().log(Level.SEVERE, null, ex);
            }

            if (component instanceof Container) {

                setFontsByComponent((Container) component, fontSize);

            }

        }

    }

    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        aboutDialog = new javax.swing.JDialog();
        jLabel2 = new javax.swing.JLabel();
        jLabel3 = new javax.swing.JLabel();
        jPanel2 = new javax.swing.JPanel();
        aboutCloseButton = new javax.swing.JButton();
        jLabel1 = new javax.swing.JLabel();
        palettePanel = new javax.swing.JPanel();
        paletteTabbedPane = new javax.swing.JTabbedPane();
        flowPalettePanel = new javax.swing.JPanel();
        beginProgramButton = new javax.swing.JButton();
        endProgramButton = new javax.swing.JButton();
        motorsPalettePanel = new javax.swing.JPanel();
        waitPalettePanel = new javax.swing.JPanel();
        othersPalettePanel = new javax.swing.JPanel();
        programmingBoardScrollPane = new javax.swing.JScrollPane();
        programmingBoard = new bobbietalk.gui.ProgrammingBoard();
        jMenuBar1 = new javax.swing.JMenuBar();
        jMenu1 = new javax.swing.JMenu();
        fileNewMenuItem = new javax.swing.JMenuItem();
        loadMenuItem = new javax.swing.JMenuItem();
        fileSaveMenuItem = new javax.swing.JMenuItem();
        saveAsMenuItem = new javax.swing.JMenuItem();
        showBshProgramMenuItem = new javax.swing.JMenuItem();
        showNxcProgramMenuItem = new javax.swing.JMenuItem();
        uploadMenuItem = new javax.swing.JMenuItem();
        runInRemoteSimulatorMenuItem = new javax.swing.JMenuItem();
        runInLocalSimulatorMenuItem = new javax.swing.JMenuItem();
        fileDumpMenuItem = new javax.swing.JMenuItem();
        exitMenuItem = new javax.swing.JMenuItem();
        jMenu2 = new javax.swing.JMenu();
        undoMenuItem = new javax.swing.JMenuItem();
        deleteMenuItem = new javax.swing.JMenuItem();
        jSeparator1 = new javax.swing.JPopupMenu.Separator();
        changeParametersMenuItem = new javax.swing.JMenuItem();
        addMenu = new javax.swing.JMenu();
        addBeginMenuItem = new javax.swing.JMenuItem();
        addForwardMenuItem = new javax.swing.JMenuItem();
        turnLeftMenuItem = new javax.swing.JMenuItem();
        turnRightMenuItem = new javax.swing.JMenuItem();
        haltMenuItem = new javax.swing.JMenuItem();
        waitMenuItem = new javax.swing.JMenuItem();
        addWaitDistanceBlockMenuItem = new javax.swing.JMenuItem();
        IfMenuItem = new javax.swing.JMenuItem();
        ifCloseMenuItem = new javax.swing.JMenuItem();
        loopBeginMenuItem = new javax.swing.JMenuItem();
        endloopMenuItem = new javax.swing.JMenuItem();
        conditionMenuItem = new javax.swing.JMenuItem();
        numberMenuItem = new javax.swing.JMenuItem();
        addEndMenuItem = new javax.swing.JMenuItem();
        ConfigureMenu = new javax.swing.JMenu();
        configureApplicationMenuItem = new javax.swing.JMenuItem();
        configureRobotMenuItem = new javax.swing.JMenuItem();
        jMenu3 = new javax.swing.JMenu();
        openHelpMenuItem = new javax.swing.JMenuItem();
        aboutMenuItem = new javax.swing.JMenuItem();

        java.util.ResourceBundle bundle = java.util.ResourceBundle.getBundle("bobbietalk/Labels"); // NOI18N
        aboutDialog.setTitle(bundle.getString("dialog.about.title")); // NOI18N
        aboutDialog.setModal(true);
        aboutDialog.setResizable(false);

        jLabel2.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
        jLabel2.setText(bundle.getString("dialog.about.summary")); // NOI18N

        jLabel3.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
        jLabel3.setIcon(new javax.swing.ImageIcon(getClass().getResource("/icons/bobbietalk_logo_300.png"))); // NOI18N

        aboutCloseButton.setText(bundle.getString("dialog.about.button.close")); // NOI18N
        aboutCloseButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                aboutCloseButtonActionPerformed(evt);
            }
        });
        jPanel2.add(aboutCloseButton);

        jLabel1.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
        jLabel1.setText("<html>\n     bobbietalk - a visual programming environment for toy robotics<br/>\n    Copyright (C) 2013  Roberto Tiella<br/>\n<br/>\n    This program is free software: you can redistribute it and/or modify<br/>\n    it under the terms of the GNU General Public License as published by<br/>\n    the Free Software Foundation, either version 3 of the License, or<br/>\n    (at your option) any later version.<br/>\n<br/>\n    This program is distributed in the hope that it will be useful,<br/>\n    but WITHOUT ANY WARRANTY; without even the implied warranty of<br/>\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the<br/>\n    GNU General Public License for more details.<br/>\n<br/>\n    You should have received a copy of the GNU General Public License<br/>\n    along with this program.  If not, see <a href=\"http://www.gnu.org/licenses\">http://www.gnu.org/licenses</a>.<br/>\n</html>");

        javax.swing.GroupLayout aboutDialogLayout = new javax.swing.GroupLayout(aboutDialog.getContentPane());
        aboutDialog.getContentPane().setLayout(aboutDialogLayout);
        aboutDialogLayout.setHorizontalGroup(
            aboutDialogLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(aboutDialogLayout.createSequentialGroup()
                .addContainerGap()
                .addGroup(aboutDialogLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(jLabel1)
                    .addGroup(aboutDialogLayout.createSequentialGroup()
                        .addComponent(jPanel2, javax.swing.GroupLayout.PREFERRED_SIZE, 471, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addGap(0, 0, Short.MAX_VALUE))
                    .addComponent(jLabel3, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                    .addComponent(jLabel2))
                .addContainerGap())
        );
        aboutDialogLayout.setVerticalGroup(
            aboutDialogLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, aboutDialogLayout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jLabel3)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jLabel2, javax.swing.GroupLayout.PREFERRED_SIZE, 117, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jLabel1)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jPanel2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addContainerGap())
        );

        setDefaultCloseOperation(javax.swing.WindowConstants.DO_NOTHING_ON_CLOSE);
        setTitle(bundle.getString("frame.main.title")); // NOI18N

        palettePanel.setBorder(javax.swing.BorderFactory.createEtchedBorder());

        flowPalettePanel.setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.LEFT));

        beginProgramButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/icons/icon_begin.png"))); // NOI18N
        beginProgramButton.setText(bundle.getString("menu.item.add.begin")); // NOI18N
        beginProgramButton.setToolTipText(bundle.getString("menu.item.add.begin.tooltip")); // NOI18N
        beginProgramButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                beginProgramButtonActionPerformed(evt);
            }
        });
        flowPalettePanel.add(beginProgramButton);

        endProgramButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/icons/icon_end.png"))); // NOI18N
        endProgramButton.setText(bundle.getString("menu.item.add.end")); // NOI18N
        endProgramButton.setToolTipText(bundle.getString("menu.item.add.end.tooltip")); // NOI18N
        endProgramButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                endProgramButtonActionPerformed(evt);
            }
        });
        flowPalettePanel.add(endProgramButton);

        paletteTabbedPane.addTab(bundle.getString("palette.tab.control.label"), new javax.swing.ImageIcon(getClass().getResource("/icons/32/start_flag.png")), flowPalettePanel); // NOI18N

        motorsPalettePanel.setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.LEFT));
        paletteTabbedPane.addTab(bundle.getString("palette.tab.motors.label"), new javax.swing.ImageIcon(getClass().getResource("/icons/32/steps_forward.png")), motorsPalettePanel); // NOI18N

        waitPalettePanel.setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.LEFT));
        paletteTabbedPane.addTab(bundle.getString("palette.tabs.wait.label"), new javax.swing.ImageIcon(getClass().getResource("/icons/32/wait_group_icon.png")), waitPalettePanel); // NOI18N

        othersPalettePanel.setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.LEFT));
        paletteTabbedPane.addTab(bundle.getString("palette.tabs.others.label"), new javax.swing.ImageIcon(getClass().getResource("/icons/32/play_tone.png")), othersPalettePanel); // NOI18N

        javax.swing.GroupLayout palettePanelLayout = new javax.swing.GroupLayout(palettePanel);
        palettePanel.setLayout(palettePanelLayout);
        palettePanelLayout.setHorizontalGroup(
            palettePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, palettePanelLayout.createSequentialGroup()
                .addContainerGap()
                .addComponent(paletteTabbedPane, javax.swing.GroupLayout.DEFAULT_SIZE, 372, Short.MAX_VALUE)
                .addContainerGap())
        );
        palettePanelLayout.setVerticalGroup(
            palettePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(palettePanelLayout.createSequentialGroup()
                .addContainerGap()
                .addComponent(paletteTabbedPane, javax.swing.GroupLayout.PREFERRED_SIZE, 133, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );

        getContentPane().add(palettePanel, java.awt.BorderLayout.SOUTH);

        programmingBoardScrollPane.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
        programmingBoardScrollPane.setPreferredSize(new java.awt.Dimension(1400, 787));

        programmingBoard.setBorder(javax.swing.BorderFactory.createEtchedBorder());
        programmingBoard.setPreferredSize(new java.awt.Dimension(2000, 768));

        javax.swing.GroupLayout programmingBoardLayout = new javax.swing.GroupLayout(programmingBoard);
        programmingBoard.setLayout(programmingBoardLayout);
        programmingBoardLayout.setHorizontalGroup(
            programmingBoardLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 1996, Short.MAX_VALUE)
        );
        programmingBoardLayout.setVerticalGroup(
            programmingBoardLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 764, Short.MAX_VALUE)
        );

        programmingBoardScrollPane.setViewportView(programmingBoard);

        getContentPane().add(programmingBoardScrollPane, java.awt.BorderLayout.CENTER);

        jMenu1.setText("File");

        fileNewMenuItem.setText(bundle.getString("menu.item.file.new")); // NOI18N
        fileNewMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                fileNewMenuItemActionPerformed(evt);
            }
        });
        jMenu1.add(fileNewMenuItem);

        loadMenuItem.setText(bundle.getString("menu.item.file.load")); // NOI18N
        loadMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                loadMenuItemActionPerformed(evt);
            }
        });
        jMenu1.add(loadMenuItem);

        fileSaveMenuItem.setText(bundle.getString("menu.item.file.save")); // NOI18N
        fileSaveMenuItem.setEnabled(false);
        fileSaveMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                fileSaveMenuItemActionPerformed(evt);
            }
        });
        jMenu1.add(fileSaveMenuItem);

        saveAsMenuItem.setText(bundle.getString("menu.item.file.saveas")); // NOI18N
        saveAsMenuItem.setEnabled(false);
        saveAsMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                saveAsMenuItemActionPerformed(evt);
            }
        });
        jMenu1.add(saveAsMenuItem);

        showBshProgramMenuItem.setText(bundle.getString("menu.item.file.showbshprogram")); // NOI18N
        showBshProgramMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                showBshProgramMenuItemActionPerformed(evt);
            }
        });
        jMenu1.add(showBshProgramMenuItem);

        showNxcProgramMenuItem.setText(bundle.getString("menu.item.file.shownxcprogram")); // NOI18N
        showNxcProgramMenuItem.setActionCommand(bundle.getString("menu.item.file.shownxcprogram")); // NOI18N
        showNxcProgramMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                showNxcProgramMenuItemActionPerformed(evt);
            }
        });
        jMenu1.add(showNxcProgramMenuItem);

        uploadMenuItem.setText(bundle.getString("menu.item.file.upload")); // NOI18N
        uploadMenuItem.setEnabled(false);
        uploadMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                uploadMenuItemActionPerformed(evt);
            }
        });
        jMenu1.add(uploadMenuItem);

        runInRemoteSimulatorMenuItem.setText(bundle.getString("menu.item.file.simulateremotely")); // NOI18N
        runInRemoteSimulatorMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                runInRemoteSimulatorMenuItemActionPerformed(evt);
            }
        });
        jMenu1.add(runInRemoteSimulatorMenuItem);

        runInLocalSimulatorMenuItem.setText(bundle.getString("menu.item.file.simulatelocally")); // NOI18N
        runInLocalSimulatorMenuItem.setEnabled(false);
        runInLocalSimulatorMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                runInLocalSimulatorMenuItemActionPerformed(evt);
            }
        });
        jMenu1.add(runInLocalSimulatorMenuItem);

        fileDumpMenuItem.setText(bundle.getString("menu.item.file.dump")); // NOI18N
        fileDumpMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                fileDumpMenuItemActionPerformed(evt);
            }
        });
        jMenu1.add(fileDumpMenuItem);

        exitMenuItem.setMnemonic('x');
        exitMenuItem.setText(bundle.getString("menu.item.file.exit")); // NOI18N
        exitMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                exitMenuItemActionPerformed(evt);
            }
        });
        jMenu1.add(exitMenuItem);

        jMenuBar1.add(jMenu1);

        jMenu2.setText(bundle.getString("menu.edit")); // NOI18N

        undoMenuItem.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_Z, java.awt.event.InputEvent.CTRL_MASK));
        undoMenuItem.setText(bundle.getString("menu.item.edit.undo")); // NOI18N
        undoMenuItem.setEnabled(false);
        undoMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                undoMenuItemActionPerformed(evt);
            }
        });
        jMenu2.add(undoMenuItem);

        deleteMenuItem.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_DELETE, 0));
        deleteMenuItem.setText(bundle.getString("menu.item.edit.delete")); // NOI18N
        deleteMenuItem.setEnabled(false);
        deleteMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                deleteMenuItemActionPerformed(evt);
            }
        });
        jMenu2.add(deleteMenuItem);
        jMenu2.add(jSeparator1);

        changeParametersMenuItem.setText(bundle.getString("menu.item.edit.change")); // NOI18N
        changeParametersMenuItem.setEnabled(false);
        changeParametersMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                changeParametersMenuItemActionPerformed(evt);
            }
        });
        jMenu2.add(changeParametersMenuItem);

        jMenuBar1.add(jMenu2);

        addMenu.setText(bundle.getString("menu.add")); // NOI18N

        addBeginMenuItem.setText(bundle.getString("menu.item.add.begin")); // NOI18N
        addBeginMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                addBeginMenuItemActionPerformed(evt);
            }
        });
        addMenu.add(addBeginMenuItem);

        addForwardMenuItem.setText(bundle.getString("menu.item.add.forward")); // NOI18N
        addForwardMenuItem.setEnabled(false);
        addForwardMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                addForwardMenuItemActionPerformed(evt);
            }
        });
        addMenu.add(addForwardMenuItem);

        turnLeftMenuItem.setText(bundle.getString("menu.item.add.turnleft")); // NOI18N
        turnLeftMenuItem.setEnabled(false);
        turnLeftMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                turnLeftMenuItemActionPerformed(evt);
            }
        });
        addMenu.add(turnLeftMenuItem);

        turnRightMenuItem.setText(bundle.getString("menu.item.add.turnright")); // NOI18N
        turnRightMenuItem.setEnabled(false);
        turnRightMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                turnRightMenuItemActionPerformed(evt);
            }
        });
        addMenu.add(turnRightMenuItem);

        haltMenuItem.setText(bundle.getString("menu.item.add.halt")); // NOI18N
        haltMenuItem.setEnabled(false);
        haltMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                haltMenuItemActionPerformed(evt);
            }
        });
        addMenu.add(haltMenuItem);

        waitMenuItem.setText(bundle.getString("menu.item.add.wait")); // NOI18N
        waitMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                waitMenuItemActionPerformed(evt);
            }
        });
        addMenu.add(waitMenuItem);

        addWaitDistanceBlockMenuItem.setText(bundle.getString("menu.item.add.waitdistance")); // NOI18N
        addWaitDistanceBlockMenuItem.setToolTipText(bundle.getString("menu.item.add.waitdistance")); // NOI18N
        addWaitDistanceBlockMenuItem.setEnabled(false);
        addWaitDistanceBlockMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                addWaitDistanceBlockMenuItemActionPerformed(evt);
            }
        });
        addMenu.add(addWaitDistanceBlockMenuItem);

        IfMenuItem.setText(bundle.getString("menu.item.add.if")); // NOI18N
        IfMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                IfMenuItemActionPerformed(evt);
            }
        });
        addMenu.add(IfMenuItem);

        ifCloseMenuItem.setText(bundle.getString("menu.item.add.ifclose")); // NOI18N
        ifCloseMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                ifCloseMenuItemActionPerformed(evt);
            }
        });
        addMenu.add(ifCloseMenuItem);

        loopBeginMenuItem.setText(bundle.getString("menu.item.add.repeatbegin")); // NOI18N
        loopBeginMenuItem.setToolTipText(bundle.getString("menu.item.add.repeatbegin.tooltip")); // NOI18N
        loopBeginMenuItem.setEnabled(false);
        loopBeginMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                loopBeginMenuItemActionPerformed(evt);
            }
        });
        addMenu.add(loopBeginMenuItem);

        endloopMenuItem.setText(bundle.getString("menu.item.add.repeatend")); // NOI18N
        endloopMenuItem.setToolTipText(bundle.getString("menu.item.add.repeatend.tooltip")); // NOI18N
        endloopMenuItem.setEnabled(false);
        endloopMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                endloopMenuItemActionPerformed(evt);
            }
        });
        addMenu.add(endloopMenuItem);

        conditionMenuItem.setText(bundle.getString("menu.item.add.condition")); // NOI18N
        conditionMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                conditionMenuItemActionPerformed(evt);
            }
        });
        addMenu.add(conditionMenuItem);

        numberMenuItem.setText(bundle.getString("menu.item.add.number")); // NOI18N
        numberMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                numberMenuItemActionPerformed(evt);
            }
        });
        addMenu.add(numberMenuItem);

        addEndMenuItem.setText(bundle.getString("menu.item.add.end")); // NOI18N
        addEndMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                addEndMenuItemActionPerformed(evt);
            }
        });
        addMenu.add(addEndMenuItem);

        jMenuBar1.add(addMenu);

        ConfigureMenu.setText(bundle.getString("menu.configure")); // NOI18N

        configureApplicationMenuItem.setText(bundle.getString("menu.item.configure.application")); // NOI18N
        configureApplicationMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                configureApplicationMenuItemActionPerformed(evt);
            }
        });
        ConfigureMenu.add(configureApplicationMenuItem);

        configureRobotMenuItem.setText(bundle.getString("menu.item.configure.robot")); // NOI18N
        configureRobotMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                configureRobotMenuItemActionPerformed(evt);
            }
        });
        ConfigureMenu.add(configureRobotMenuItem);

        jMenuBar1.add(ConfigureMenu);

        jMenu3.setText(bundle.getString("menu.help")); // NOI18N

        openHelpMenuItem.setText(bundle.getString("menu.item.help.open")); // NOI18N
        openHelpMenuItem.setEnabled(false);
        openHelpMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                openHelpMenuItemActionPerformed(evt);
            }
        });
        jMenu3.add(openHelpMenuItem);

        aboutMenuItem.setText(bundle.getString("menu.item.help.about")); // NOI18N
        aboutMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                aboutMenuItemActionPerformed(evt);
            }
        });
        jMenu3.add(aboutMenuItem);

        jMenuBar1.add(jMenu3);

        setJMenuBar(jMenuBar1);

        pack();
    }// </editor-fold>//GEN-END:initComponents

    private void addBeginMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_addBeginMenuItemActionPerformed
        try {
            addBlock("begin");
            // addBeginMenuItem.setEnabled(false);
        } catch (BobbietalkException ex) {
            Utilities.getLogger().log(Level.SEVERE, null, ex);
        }
    }//GEN-LAST:event_addBeginMenuItemActionPerformed

    private void addEndMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_addEndMenuItemActionPerformed
        try {
            addBlock("end");
            // addEndMenuItem.setEnabled(false);
        } catch (BobbietalkException ex) {
            Utilities.getLogger().log(Level.SEVERE, null, ex);
        }

    }//GEN-LAST:event_addEndMenuItemActionPerformed

    private void exitMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_exitMenuItemActionPerformed
        checkProgramSavedAndAskUserToClose();
    }//GEN-LAST:event_exitMenuItemActionPerformed

    private void addForwardMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_addForwardMenuItemActionPerformed
        try {
            addBlock("forward");
        } catch (BobbietalkException ex) {
            Utilities.getLogger().log(Level.SEVERE, null, ex);
        }
    }//GEN-LAST:event_addForwardMenuItemActionPerformed

    private void showBshProgramMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_showBshProgramMenuItemActionPerformed

        Compiler c = instantiateCompiler("bsh");
        showProgram(c);

    }//GEN-LAST:event_showBshProgramMenuItemActionPerformed

    private void haltMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_haltMenuItemActionPerformed
        try {
            addBlock("halt");
        } catch (BobbietalkException ex) {
            Utilities.getLogger().log(Level.SEVERE, null, ex);
        }
    }//GEN-LAST:event_haltMenuItemActionPerformed

    private void waitMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_waitMenuItemActionPerformed
        try {
            addBlock("wait");
        } catch (BobbietalkException ex) {
            Utilities.getLogger().log(Level.SEVERE, null, ex);
        }
    }//GEN-LAST:event_waitMenuItemActionPerformed

    private void deleteMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_deleteMenuItemActionPerformed

        programmingBoard.deleteSelectedBlock();

    }//GEN-LAST:event_deleteMenuItemActionPerformed

    private void turnLeftMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_turnLeftMenuItemActionPerformed
        try {
            addBlock("turnleft");
        } catch (BobbietalkException ex) {
            Utilities.getLogger().log(Level.SEVERE, null, ex);
        }
    }//GEN-LAST:event_turnLeftMenuItemActionPerformed

    private void turnRightMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_turnRightMenuItemActionPerformed
        try {
            addBlock("turnright");
        } catch (BobbietalkException ex) {
            Utilities.getLogger().log(Level.SEVERE, null, ex);
        }
    }//GEN-LAST:event_turnRightMenuItemActionPerformed

    private void saveAsMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_saveAsMenuItemActionPerformed

        JFileChooser chooser = new JFileChooser(System.getProperty("user.dir"));
        chooser.setSelectedFile(programFile);

        if (chooser.showSaveDialog(this) == JFileChooser.APPROVE_OPTION) {

            programmingBoard.saveProgram(chooser.getSelectedFile());
            programFile = chooser.getSelectedFile();

            neverSaved = false;

            updateTitle();
            updateMenuState();

        }


    }//GEN-LAST:event_saveAsMenuItemActionPerformed

    private void loadMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_loadMenuItemActionPerformed

        boolean continueWithLoading = true;

        if (programmingBoard.isProgramChanged() && !programmingBoard.isProgramEmpty()) {

            userDialog.setSummaryLabel(bundle.getString("dialog.load.confirm.summary"));
            userDialog.setDetailsLabel(bundle.getString("dialog.load.confirm.details"));
            userDialog.setOkLabel(bundle.getString("dialog.confirm.yes"));
            userDialog.setCancelLabel(bundle.getString("dialog.confirm.no"));
            userDialog.setCancelButtonVisible(true);
            userDialog.pack();
            userDialog.setVisible(true);

            if (!userDialog.isUserConfirmed()) {
                continueWithLoading = false;
            }

        }

        if (continueWithLoading) {

            JFileChooser chooser = new JFileChooser(System.getProperty("user.dir"));

            if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {

                try {
                    programmingBoard.loadProgram(chooser.getSelectedFile());
                    neverSaved = false;
                    updateMenuState();
//                saveAsMenuItem.setEnabled(true);
                    programFile = chooser.getSelectedFile();
                    updateTitle();
                } catch (FileNotFoundException ex) {
                    showError("can't load program", ex);
                }

            }

        }


    }//GEN-LAST:event_loadMenuItemActionPerformed

    private void undoMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_undoMenuItemActionPerformed

        programmingBoard.undo();

    }//GEN-LAST:event_undoMenuItemActionPerformed

    private void IfMenuItemActionPerformed(java.awt.event.ActionEvent evt) {
        try {
            addBlock("ifopen");
        } catch (BobbietalkException ex) {
            Utilities.getLogger().log(Level.SEVERE, null, ex);
        }
    }

    private void configureApplicationMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_configureApplicationMenuItemActionPerformed

        configureApplicationDialog.setSelectedLanguge(language);
        configureApplicationDialog.setSelectedFontSize(fontSize);

        configureApplicationDialog.setVisible(true);

        if (configureApplicationDialog.getUserAction() == configureApplicationDialog.USER_ACCEPTED) {
            try {

                language = configureApplicationDialog.getSelectedLanguge();
                fontSize = configureApplicationDialog.getSelectedFontSize();

                updateApplicationConfiguration();
            } catch (JAXBException ex) {
                Utilities.getLogger().log(Level.SEVERE, null, ex);
            } catch (IOException ex) {
                Utilities.getLogger().log(Level.SEVERE, null, ex);
            } catch (BobbietalkException ex) {
                Utilities.getLogger().log(Level.SEVERE, null, ex);
            }

        }
    }//GEN-LAST:event_configureApplicationMenuItemActionPerformed

    private void conditionMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_conditionMenuItemActionPerformed
        try {
            addBlock("condition");
        } catch (BobbietalkException ex) {
            Utilities.getLogger().log(Level.SEVERE, null, ex);
        }

    }//GEN-LAST:event_conditionMenuItemActionPerformed

    private void ifCloseMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_ifCloseMenuItemActionPerformed
        try {
            addBlock("ifclose");
        } catch (BobbietalkException ex) {
            Utilities.getLogger().log(Level.SEVERE, null, ex);
        }

    }//GEN-LAST:event_ifCloseMenuItemActionPerformed

    private void numberMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_numberMenuItemActionPerformed
        try {
            addBlock("number");
        } catch (BobbietalkException ex) {
            Utilities.getLogger().log(Level.SEVERE, null, ex);
        }
    }//GEN-LAST:event_numberMenuItemActionPerformed

    private void uploadMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_uploadMenuItemActionPerformed

        Compiler c = instantiateNxcCompiler();

        List<ProgrammingBlock> blocks = programmingBoard.getBlocks();
        ArrayList<String> messages = new ArrayList<>();

        if (!c.compile(blocks, messages, false)) {

            userDialog.setSummaryLabel(bundle.getString("dialog.compilation.summary"));
            userDialog.setCancelButtonVisible(false);
            userDialog.setOkLabel(bundle.getString("dialog.compilation.ok.label"));

            StringBuilder b = new StringBuilder();
            filterNxcMessagesForUser(messages, b);

            userDialog.setDetailsLabel(b.toString());

            userDialog.pack();

            userDialog.setVisible(true);

        }
    }//GEN-LAST:event_uploadMenuItemActionPerformed

    private void loopBeginMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_loopBeginMenuItemActionPerformed
        try {
            addBlock("repeatbegin");
        } catch (BobbietalkException ex) {
            Utilities.getLogger().log(Level.SEVERE, null, ex);
        }

    }//GEN-LAST:event_loopBeginMenuItemActionPerformed

    private void endloopMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_endloopMenuItemActionPerformed
        try {
            addBlock("repeatend");
        } catch (BobbietalkException ex) {
            Utilities.getLogger().log(Level.SEVERE, null, ex);
        }

    }//GEN-LAST:event_endloopMenuItemActionPerformed

    private void fileNewMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_fileNewMenuItemActionPerformed

        boolean continueWithNew = true;

        if (programmingBoard.isProgramChanged()) {


            userDialog.setSummaryLabel(bundle.getString("dialog.new.confirm.summary"));
            userDialog.setDetailsLabel(bundle.getString("dialog.new.confirm.details"));
            userDialog.setOkLabel(bundle.getString("dialog.confirm.yes"));
            userDialog.setCancelLabel(bundle.getString("dialog.confirm.no"));
            userDialog.setCancelButtonVisible(true);
            userDialog.pack();
            userDialog.setVisible(true);

            if (!userDialog.isUserConfirmed()) {
                continueWithNew = false;
            }

        }

        if (continueWithNew) {

            newProgram();
            neverSaved = true;
            updateTitle();
            updateMenuState();

        }


    }//GEN-LAST:event_fileNewMenuItemActionPerformed

    private void endProgramButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_endProgramButtonActionPerformed
        try {
            addBlock("end");
        } catch (BobbietalkException ex) {
            Utilities.getLogger().log(Level.SEVERE, null, ex);
        }
    }//GEN-LAST:event_endProgramButtonActionPerformed

    private void beginProgramButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_beginProgramButtonActionPerformed
        try {
            addBlock("begin");
        } catch (BobbietalkException ex) {
            Utilities.getLogger().log(Level.SEVERE, null, ex);
        }
    }//GEN-LAST:event_beginProgramButtonActionPerformed

    private void configureRobotMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_configureRobotMenuItemActionPerformed

        configureRobotDialog.setVisible(true);

//        if (configureRobotDialog.getUserAction() == configureRobotDialog.USER_ACCEPTED) {
//            updateRobotConfiguration();
//
//        }

    }//GEN-LAST:event_configureRobotMenuItemActionPerformed

    private void runInRemoteSimulatorMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_runInRemoteSimulatorMenuItemActionPerformed

        InterpreterController i = new InterpreterController();

        List<ProgrammingBlock> blocks = programmingBoard.getBlocks();
        ArrayList<String> messages = new ArrayList<>();

        if (!i.init(blocks, messages)) {

            userDialog.setSummaryLabel(bundle.getString("dialog.compilation.summary"));
            userDialog.setCancelButtonVisible(false);
            userDialog.setOkLabel(bundle.getString("dialog.compilation.ok.label"));

            StringBuilder b = new StringBuilder();

            for (String m : messages) {
                b.append(m).append("\n");
            }

            userDialog.setDetailsLabel(b.toString());

            userDialog.pack();

            userDialog.setVisible(true);

        } else {

            interpreterControlDialog.setInterpreterControl(i);
            interpreterControlDialog.open();

        }

    }//GEN-LAST:event_runInRemoteSimulatorMenuItemActionPerformed

    private void addWaitDistanceBlockMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_addWaitDistanceBlockMenuItemActionPerformed

        try {
            addBlock("waitdistance");
        } catch (BobbietalkException ex) {
            Utilities.getLogger().log(Level.SEVERE, null, ex);
        }

    }//GEN-LAST:event_addWaitDistanceBlockMenuItemActionPerformed

    private void fileDumpMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_fileDumpMenuItemActionPerformed

        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw);

        programmingBoard.dumpContent(pw);

        userDialog.setDetailsLabel(sw.toString());
        userDialog.setSummaryLabel("Program dump");

        userDialog.setVisible(true);

    }//GEN-LAST:event_fileDumpMenuItemActionPerformed

    private void fileSaveMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_fileSaveMenuItemActionPerformed

        if (neverSaved) {

            saveAsMenuItemActionPerformed(evt);

        } else {

            programmingBoard.saveProgram(programFile);

            updateTitle();
            updateMenuState();

        }

    }//GEN-LAST:event_fileSaveMenuItemActionPerformed

    private void aboutMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_aboutMenuItemActionPerformed

        aboutDialog.setVisible(true);

    }//GEN-LAST:event_aboutMenuItemActionPerformed

    private void aboutCloseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_aboutCloseButtonActionPerformed

        aboutDialog.setVisible(false);

    }//GEN-LAST:event_aboutCloseButtonActionPerformed

    private void runInLocalSimulatorMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_runInLocalSimulatorMenuItemActionPerformed

        Compiler c = instantiateBshCompiler();

        List<ProgrammingBlock> blocks = programmingBoard.getBlocks();
        ArrayList<String> messages = new ArrayList<>();

        StringBuilder code = new StringBuilder();

        if (c.compileOnly(blocks, messages, code, false)) {

            localSimulationWindow.runCode(code.toString());
            localSimulationWindow.setVisible(true);

        } else {

            userDialog.setSummaryLabel(bundle.getString("dialog.compilation.summary"));
            userDialog.setCancelButtonVisible(false);
            userDialog.setOkLabel(bundle.getString("dialog.compilation.ok.label"));

            StringBuilder b = new StringBuilder();

            for (String m : messages) {
                b.append(m).append("\n");
            }

            userDialog.setDetailsLabel(b.toString());
            userDialog.pack();
            userDialog.setVisible(true);

        }




    }//GEN-LAST:event_runInLocalSimulatorMenuItemActionPerformed

    private void showNxcProgramMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_showNxcProgramMenuItemActionPerformed

        Compiler c = instantiateCompiler("nxc");
        showProgram(c);

    }//GEN-LAST:event_showNxcProgramMenuItemActionPerformed

    private void openHelpMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_openHelpMenuItemActionPerformed
        try {
            Desktop.getDesktop().browse(new URI(bundle.getString("help.online.uri")));
        } catch (IOException | URISyntaxException ex) {
            Utilities.getLogger().log(Level.SEVERE, null, ex);
        }

    }//GEN-LAST:event_openHelpMenuItemActionPerformed

    private void changeParametersMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_changeParametersMenuItemActionPerformed

        ProgrammingBlock selected = programmingBoard.getSelectedBlock();

        if (selected instanceof ParametrizedBlock) {
            openBlockEditingDialog((ParametrizedBlock) selected);
        } else {
            throw new RuntimeException("trying to change parameters to a non parametrized block:" + selected.getClass());
        }


    }//GEN-LAST:event_changeParametersMenuItemActionPerformed
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JMenu ConfigureMenu;
    private javax.swing.JMenuItem IfMenuItem;
    private javax.swing.JButton aboutCloseButton;
    private javax.swing.JDialog aboutDialog;
    private javax.swing.JMenuItem aboutMenuItem;
    private javax.swing.JMenuItem addBeginMenuItem;
    private javax.swing.JMenuItem addEndMenuItem;
    private javax.swing.JMenuItem addForwardMenuItem;
    private javax.swing.JMenu addMenu;
    private javax.swing.JMenuItem addWaitDistanceBlockMenuItem;
    private javax.swing.JButton beginProgramButton;
    private javax.swing.JMenuItem changeParametersMenuItem;
    private javax.swing.JMenuItem conditionMenuItem;
    private javax.swing.JMenuItem configureApplicationMenuItem;
    private javax.swing.JMenuItem configureRobotMenuItem;
    private javax.swing.JMenuItem deleteMenuItem;
    private javax.swing.JButton endProgramButton;
    private javax.swing.JMenuItem endloopMenuItem;
    private javax.swing.JMenuItem exitMenuItem;
    private javax.swing.JMenuItem fileDumpMenuItem;
    private javax.swing.JMenuItem fileNewMenuItem;
    private javax.swing.JMenuItem fileSaveMenuItem;
    private javax.swing.JPanel flowPalettePanel;
    private javax.swing.JMenuItem haltMenuItem;
    private javax.swing.JMenuItem ifCloseMenuItem;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JLabel jLabel2;
    private javax.swing.JLabel jLabel3;
    private javax.swing.JMenu jMenu1;
    private javax.swing.JMenu jMenu2;
    private javax.swing.JMenu jMenu3;
    private javax.swing.JMenuBar jMenuBar1;
    private javax.swing.JPanel jPanel2;
    private javax.swing.JPopupMenu.Separator jSeparator1;
    private javax.swing.JMenuItem loadMenuItem;
    private javax.swing.JMenuItem loopBeginMenuItem;
    private javax.swing.JPanel motorsPalettePanel;
    private javax.swing.JMenuItem numberMenuItem;
    private javax.swing.JMenuItem openHelpMenuItem;
    private javax.swing.JPanel othersPalettePanel;
    private javax.swing.JPanel palettePanel;
    private javax.swing.JTabbedPane paletteTabbedPane;
    private bobbietalk.gui.ProgrammingBoard programmingBoard;
    private javax.swing.JScrollPane programmingBoardScrollPane;
    private javax.swing.JMenuItem runInLocalSimulatorMenuItem;
    private javax.swing.JMenuItem runInRemoteSimulatorMenuItem;
    private javax.swing.JMenuItem saveAsMenuItem;
    private javax.swing.JMenuItem showBshProgramMenuItem;
    private javax.swing.JMenuItem showNxcProgramMenuItem;
    private javax.swing.JMenuItem turnLeftMenuItem;
    private javax.swing.JMenuItem turnRightMenuItem;
    private javax.swing.JMenuItem undoMenuItem;
    private javax.swing.JMenuItem uploadMenuItem;
    private javax.swing.JMenuItem waitMenuItem;
    private javax.swing.JPanel waitPalettePanel;
    // End of variables declaration//GEN-END:variables

    private ResourceBundle getBundle() {

        return ResourceBundle.getBundle("Labels");

    }

    @Override
    public void handleUnduableActionEvent(UnduableActionEvent ev) {

        switch (ev.getType()) {
            case QUEUE_EMPTY:
                undoMenuItem.setEnabled(false);
                break;

            case QUEUE_NOT_EMPTY:
                undoMenuItem.setEnabled(true);
                break;

        }

    }

    @Override
    public void handleSelectionChangedEvent(SelectionChangedEvent ev) {

        boolean isParameter = false;

        if (ev.getSelectionSize() > 0) {

            deleteMenuItem.setEnabled(true);

            if (programmingBoard.getSelectedBlock() instanceof ParametrizedBlock) {

                isParameter = true;
            }
        } else {
            deleteMenuItem.setEnabled(false);
        }

        if (isParameter) {
            changeParametersMenuItem.setEnabled(true);
        } else {
            changeParametersMenuItem.setEnabled(false);
        }

    }

    boolean openNumberDialog(int[] answer) {

        userInputDialog.setNumber(answer[0]);
        userInputDialog.setVisible(true);


        if (userInputDialog.isUserConfirmed()) {

            answer[0] = userInputDialog.getNumber();


        }

        return userInputDialog.isUserConfirmed();

    }

    private void updateApplicationConfiguration() throws JAXBException, IOException, BobbietalkException {

        String p = pushCurrentProgram();

        if (language.equals("English")) {

            Locale.setDefault(Locale.ENGLISH);

        } else if (language.equals("Italiano")) {

            Locale.setDefault(Locale.ITALY);

        }

        myInitComponents();

        paletteTabbedPane.setVisible(configureApplicationDialog.isPaletteVisible()); // TODO it doesn't work
        addMenu.setVisible(!configureApplicationDialog.isPaletteVisible());
        programmingBoard.setGridEnabled(configureApplicationDialog.isGridEnabled());

        popCurrentProgram(p);


    }

    private void updateMenuState() {



        if (Desktop.isDesktopSupported()) {
            // TODO this can be done once 
            openHelpMenuItem.setEnabled(true);

        }

        if (programmingBoard.isProgramEmpty()) {

            fileSaveMenuItem.setEnabled(false);
            saveAsMenuItem.setEnabled(false);
            loadMenuItem.setEnabled(true);
            uploadMenuItem.setEnabled(false);
            showBshProgramMenuItem.setEnabled(false);
            showNxcProgramMenuItem.setEnabled(false);
            fileNewMenuItem.setEnabled(false);
            runInRemoteSimulatorMenuItem.setEnabled(false);
            runInLocalSimulatorMenuItem.setEnabled(false);

        } else {

            uploadMenuItem.setEnabled(true);
            showBshProgramMenuItem.setEnabled(true);
            showNxcProgramMenuItem.setEnabled(true);
            fileNewMenuItem.setEnabled(true);
            runInRemoteSimulatorMenuItem.setEnabled(true);
            runInLocalSimulatorMenuItem.setEnabled(true);

            if (programmingBoard.isProgramChanged()) {

                fileSaveMenuItem.setEnabled(true);
                saveAsMenuItem.setEnabled(true);

            } else {
                saveAsMenuItem.setEnabled(false);
                fileSaveMenuItem.setEnabled(false);

            }

        }

    }

    private void addBlock(String name) throws BobbietalkException {
        programmingBoard.addBlock(name);
        updateTitle();
        updateMenuState();

    }

    private Compiler instantiatePseudoCodeCompiler() {
        final PseudoCodeCompiler compiler = new PseudoCodeCompiler(properties.getProperty("nxc.tmpdir.path"));

        compiler.setProgramName(getProgramName().replaceAll(".bdo", ""));

        return compiler;
    }

    private Compiler instantiateNxcCompiler() {
        
        final NxcCompiler nxcCompiler = new NxcCompiler(properties.getProperty("nxc.executable.path"),
                properties.getProperty("nxc.tmpdir.path"),
                properties.getProperty("nxc.firmwareversion"), new ProgramLoader(blockFactory));

        nxcCompiler.setProgramName(getProgramName().replaceAll(".bdo", ""));

        RobotConfiguration rc = getRobotConfiguration();
        nxcCompiler.setRobotConfiguration(rc);

        return nxcCompiler;
    }

    private Compiler instantiateBshCompiler() {

        final BshCompiler bshTranslator = new BshCompiler(properties.getProperty("nxc.tmpdir.path"));

        bshTranslator.setProgramName(getProgramName().replaceAll(".bdo", ""));

        RobotConfiguration rc = getRobotConfiguration();
        bshTranslator.setRobotConfiguration(rc);

        return bshTranslator;
    }

    private void newProgram() {
        programFile = new File(System.getProperty("user.dir"), bundle.getString("string.unnamed.file") + untitledFileNumber + ".bdo");
        untitledFileNumber++;
        programmingBoard.clearProgram();


    }

    private void updateTitle() {

        String oldTitle = getTitle();
        setTitle(oldTitle.replaceAll(":.*", "") + ": " + (programmingBoard.isProgramChanged() ? "* " : "") + getProgramName());

    }
    
    
     private String getCountryCode() throws MissingResourceException {
         
         // TODO cloned by BlockFactory
         
        return Locale.getDefault().getLanguage().toLowerCase();
    }
     
     
     private String tooltipForCurrentLanguage(List<Palette.BlockClass.Tooltip> tooltip) {
         
         // TODO cloned by BlockFactory

        final String country = getCountryCode();

        for (Palette.BlockClass.Tooltip l : tooltip) {


            if (l.getLang().equals(country)) {

                return l.getValue();

            }

        }

        throw new IllegalArgumentException("no label for current local:" + country);

    }

    private void setupPaletteMenu() throws JAXBException, BobbietalkException {

        blockFactory = new BlockFactory(getClass().getClassLoader().getResourceAsStream(paletteFilename));

        List<String> types = blockFactory.getTypes();

//        JMenu genericMenu = new JMenu();
//        
//
//        for (String t : types) {
//
//            BlockMenuItemActionListener l = new BlockMenuItemActionListener(this, t);
//
//            JMenuItem m = new JMenuItem(blockFactory.getLocalizedBlockTypeName(t));
//            m.addActionListener(l);
//
//            genericMenu.add(m);
//
//        }

        for (String t : types) {

            Palette.BlockClass blockData = blockFactory.getBlockData(t);

            if (!blockData.getGroup().isEmpty()) {

                try {
                    JButton button = new JButton(blockFactory.getLocalizedBlockTypeName(t));



                    String iconName = blockData.getActionIcon();

                    if (iconName.equals("")) {
                        iconName = "bobbietalk_logo_no_shadow";
                    }

                    BufferedImage icon = ImageIO.read(getClass().getResourceAsStream(String.format("/icons/%s.png", iconName)));
                    button.setIcon(new ImageIcon(icon));
                    button.setToolTipText(tooltipForCurrentLanguage(blockData.getTooltip()));


                    BlockMenuItemActionListener l = new BlockMenuItemActionListener(this, t);
                    button.addActionListener(l);
                   

                    String group = blockData.getGroup();

                    JPanel groupPanel = null;

                    if (group.equals("flow")) {
                        groupPanel = flowPalettePanel;
                    } else if (group.equals("motors")) {
                        groupPanel = motorsPalettePanel;
                    } else if (group.equals("wait")) {
                        groupPanel = waitPalettePanel;
                    } else if (group.equals("others")) {
                        groupPanel = othersPalettePanel;
                    } else {
                        throw new RuntimeException("unknown group:" + group);
                    }

                    groupPanel.add(button);

                } catch (IOException | IllegalArgumentException ex) {

                    throw new BobbietalkException("can't initialize with type:" + t, ex);

                }

            }

        }

    }

    public void addFromFactory(String type) {

        ProgrammingBlock b = blockFactory.newInstance(type);
        programmingBoard.addGeneric(b);
        updateTitle();
        updateMenuState();

    }

    public void openBlockEditingDialog(ParametrizedBlock b) {

        blockEditingDialog.setBlock(b);
        blockEditingDialog.setVisible(true);


        if (blockEditingDialog.isUserConfirmed()) {

            blockEditingDialog.saveBlock(b);
            programmingBoard.setProgramChanged(true);

            updateMenuState();
            updateTitle();

            b.repaint();

        }


    }

    private RobotConfiguration getRobotConfiguration() {

        RobotConfiguration rc = new RobotConfiguration();

        rc.setColorSensorPort(configureRobotDialog.getColorSensorPort());
        rc.setDistanceSensorPort(configureRobotDialog.getDistanceSensorPort());
        rc.setTouch1SensorPort(configureRobotDialog.getTouch1SensorPort());
        rc.setTouch2SensorPort(configureRobotDialog.getTouch2SensorPort());

        rc.setLeftMotorPort(configureRobotDialog.getLeftMotorPort());
        rc.setRightMotorPort(configureRobotDialog.getRightMotorPort());
        rc.setOtherMotorPort(configureRobotDialog.getOtherMotorPort());

        return rc;
    }

    public BlockFactory getBlockFactory() {

        return blockFactory;

    }

    @Override
    public void handleProgramChangedEvent(ProgramChangedEvent ev) {
        updateMenuState();
        updateTitle();
    }

    private void closeApplication() {

        disposeDialogs();
        this.dispose();

        System.out.println("bobbietalk ended.");

        // TODO the program should terminated by itself 
        System.exit(0);

    }

    private Compiler instantiateCompiler(String type) {

        if (type.equals("nxc")) {

            return instantiateNxcCompiler();

        } else if (type.equals("bsh")) {

            return instantiateBshCompiler();

        } else {
            throw new IllegalArgumentException("unknown compiler type:" + type);
        }

    }

    private void filterNxcMessagesForUser(ArrayList<String> messages, StringBuilder b) {

        boolean knownProblem = false;

        for (String m : messages) {
            // b.append(m).append("\n");
            if (m.contains("# Status: Download failed")) {
                b.append(bundle.getString("nxc.error.message.download_failed"));
                knownProblem = true;
            }

        }

        if (!knownProblem && messages.get(0).startsWith("#")) {
            b.append(bundle.getString("nxc.error.message.unknown_problem")).append('\n');
        }

        if (!knownProblem) {
            for (String m : messages) {
                b.append(m).append('\n');
            }
        }

    }

    private void checkProgramSavedAndAskUserToClose() {
        if (programmingBoard.isProgramChanged() && !programmingBoard.isProgramEmpty()) {

            userDialog.setOkLabel(bundle.getString("dialog.exit.confirm.ok.label"));
            userDialog.setCancelLabel(bundle.getString("dialog.exit.confirm.cancel.label"));
            userDialog.setCancelButtonVisible(true);
            userDialog.setSummaryLabel(bundle.getString("dialog.exit.confirm.summary"));
            userDialog.setDetailsLabel(bundle.getString("dialog.exit.confirm.details"));
            userDialog.pack();
            userDialog.setVisible(true);

            if (userDialog.isUserConfirmed()) {
                closeApplication();
            }

        } else {
            closeApplication();
        }
    }

    private void showProgram(Compiler c) {
        List<ProgrammingBlock> blocks = programmingBoard.getBlocks();
        ArrayList<String> messages = new ArrayList<>();
        StringBuilder code = new StringBuilder();

        if (!c.compileOnly(blocks, messages, code, false)) {

            userDialog.setSummaryLabel("Compilation errors");

            StringBuilder b = new StringBuilder();

            for (String m : messages) {
                b.append(m).append("\n");
            }

            userDialog.setDetailsLabel(b.toString());
            userDialog.pack();
            userDialog.setVisible(true);

        } else {

            userDialog.setSummaryLabel(bundle.getString("dialog.sourcecode.summary"));
            userDialog.setDetailsLabel(code.toString());
            userDialog.setVisible(true);
        }
    }

    private void disposeDialogs() {
        /*
         * TODO a hard way to reset language and font size
         */

        // dispose and rebuild dialogs

        userDialog.dispose();
        userInputDialog.dispose();
        configureApplicationDialog.dispose();
        configureRobotDialog.dispose();
        interpreterControlDialog.dispose();
        blockEditingDialog.dispose();
        localSimulationWindow.dispose();
    }

    private String pushCurrentProgram() {


        if (programmingBoard != null) {


            return programmingBoard.serializeProgram();

        }

        return null;


    }

    private void popCurrentProgram(String prg) {
        if (prg != null) {
            programmingBoard.loadProgram(new StringReader(prg));
            updateMenuState();
        }
    }

    private void showError(String msg, Throwable ex) {

        userDialog.setSummaryLabel("Error");
        userDialog.setCancelButtonVisible(false);
        userDialog.setOkLabel(bundle.getString("dialog.compilation.ok.label"));

        StringBuilder b = new StringBuilder();

        b.append(msg).append('\n');
        b.append(ex.getLocalizedMessage()).append('\n');

        userDialog.setDetailsLabel(b.toString());
        userDialog.pack();
        userDialog.setVisible(true);

    }

    private String getProgramName() {

        return programFile.getName();

    }
}
